# 监控用的
import psutil
import threading
import time
import json
import logging
import sys
from socket import socket, AF_INET, SOCK_STREAM

config = json.loads(open(r'./config.json', encoding='utf-8').read())

monitor = config['monitor']
if monitor == -1:
    sys.exit(0)
server = config['server']
if monitor == -1:
    sys.exit(0)
name = config['name']
HOST = config["sendIP"]
PORT = config["sendPort"]
maxCpu = config["maxCpu"]
maxMemory = config["maxMemory"]
maxDisk = config["maxDisk"]
ADDR = (HOST, PORT)

# 创建一个logger
logger = logging.getLogger('DeivceInfo')
logger.setLevel(logging.DEBUG)

# 创建一个handler，用于写入日志文件
fh = logging.FileHandler('see.log')
fh.setLevel(logging.DEBUG)

# 定义handler的输出格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)

# 给logger添加handler
logger.addHandler(fh)

# 初始化这一刻的硬盘和网络使用情况
initdisk = psutil.disk_io_counters()
initNet = psutil.net_io_counters()


# 单位换算 内存 字节->对应单位
def bytes2human(n):
    symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
    prefix = {}
    for i, s in enumerate(symbols):
        prefix[s] = 1 << (i + 1) * 10
    for s in reversed(symbols):
        if n >= prefix[s]:
            value = float(n) / prefix[s]
            return '%.1f%s' % (value, s)
    return '%sB' % n


# 获取最新的硬盘信息
def diskInfo():
    res = []
    disks = psutil.disk_partitions()
    for disk in disks:
        try:
            # | (disk.device == 'E:\\') | (disk.device == 'G:\\')
            if (disk.fstype == 'UDF') | (disk.fstype == 'CDFS'):
                continue
            useInfo = psutil.disk_usage(disk.device)
            Val = {"drive": disk.device,
                   "all": useInfo.total,
                   "used": useInfo.used,
                   "free": useInfo.free,
                   "percent": useInfo.percent}
            res.append(Val)
        except Exception as r:
            logger.error('读取硬盘信息出现问题：' + str(r))
            continue
    return res


# Cpu信息 (CPU占用率为过去一分钟内的平均占用)
def cpuInfo():
    res = {"core": psutil.cpu_count(logical=False),
           "logicCore": psutil.cpu_count(),
           "cpuUse": psutil.cpu_percent(interval=None)}
    return res


# 内存信息
def memoryInfo():
    svmem = psutil.virtual_memory()
    res = {"all": svmem.total,
           "used": svmem.used,
           "available": svmem.available,
           "percent": svmem.percent}
    return res


# 硬盘IO获取 (若获取IO报错需要在电脑上执行cmd命令  -> diskperf -y)
def diskIO():
    global initdisk
    t2 = psutil.disk_io_counters()
    read_bytes = round((t2.read_bytes - initdisk.read_bytes)/60, 2)
    write_bytes = round((t2.write_bytes - initdisk.write_bytes)/60, 2)
    tps = round((t2.read_count + t2.write_count - initdisk.read_count - initdisk.write_count)/60, 2)
    initdisk = t2
    res = {
        "read": read_bytes,
        "write": write_bytes,
        "tps": tps
    }
    return res


# NET IO获取
def netIO():
    global initNet
    t2 = psutil.net_io_counters()
    send_bytes = round((t2.bytes_sent - initNet.bytes_sent)/60, 2)
    recv_bytes = round((t2.bytes_recv - initNet.bytes_recv)/60, 2)
    initNet = t2
    res = {
        "send": send_bytes,
        "recv": recv_bytes,
    }
    return res


# 心跳
def selfHeart():
    try:
        tcpCliSock = socket(AF_INET, SOCK_STREAM)
        tcpCliSock.connect(ADDR)
        data = {"type": "heart", "monitor": monitor}
        tcpCliSock.send(str(data).encode())
        tcpCliSock.close()
    except Exception as r:
        logger.error(str(r) + '--------selfHeart')
    finally:
        threading.Timer(60, selfHeart, ()).start()


def sendMSGNoSpeak(msg, text):
    sendMSGCore(msg, True, False, text, '')


def sendMSGNoText(msg, speak):
    sendMSGCore(msg, False, True, '', speak)


def sendMSGAllNo(msg):
    sendMSGCore(msg, False, False, '', '')


def sendMSG(msg, text, speak):
    sendMSGCore(msg, True, True, text, speak)


def sendMSGCore(msg, isText, isSpeak, text, speak):
    try:
        tcpCliSock = socket(AF_INET, SOCK_STREAM)
        tcpCliSock.connect(ADDR)
        data = {"type": "forward", "monitor": monitor, "msg": msg, "isText": isText, "isSpeak": isSpeak, "text": text, "speak": speak}
        json.dumps(data)
        tcpCliSock.send(str(json.dumps(data)).encode())
        tcpCliSock.close()
    except Exception as r:
        logger.error(str(r) + '--------sendMSG')
        print('请求失败，继续请求')


# 推送
def push(data):
    try:
        tcpCliSock = socket(AF_INET, SOCK_STREAM)
        tcpCliSock.connect(ADDR)
        data = {"type": "serverPush", "server": server, "data": data}
        tcpCliSock.send(str(data).encode())
        tcpCliSock.close()
    except Exception as r:
        logger.error(str(r) + '--------push')
        print('请求失败，继续请求')


# 检查开启
def checkStateStart():
    try:
        do = threading.Thread(target=checkState, )
        do.start()
        while do.is_alive():
            time.sleep(0.5)
    except Exception as r:
        logger.error(str(r))
    finally:
        threading.Timer(60, checkStateStart, ()).start()


# 检查硬盘和内存状态，及时给予警告
def checkState():
    disks = diskInfo()
    info = ''
    for disk in disks:
        if disk['percent'] > maxDisk:
            info += ('\n --- ' +
                     '\n-  硬盘：' + str(disk['drive']) +
                     '\n-  **使用率超过 ' + str(maxDisk) + '%**' +
                     '\n-  总计：' + str(bytes2human(disk['all'])) +
                     '\n-  已使用：' + str(bytes2human(disk['used'])) +
                     '\n-  使用率：' + str(disk['percent']) + '%' +
                     '\n-  剩余：' + str(bytes2human(disk['free'])) +
                     '\n-  请及时处理。')
    if len(info) > 0:
        sendMSG('**' + name + '** \n --- ' + info, name + '\n' + info, '磁盘占用过高,注意查看')
    memory = memoryInfo()
    if memory['percent'] > maxMemory:
        sendMSG('**' + name + '** '
                '\n-  内存' +
                '\n-  **使用率超过 ' + str(maxMemory) + '%**' +
                '\n-  总计：' + str(bytes2human(memory['all'])) +
                '\n-  已使用：' + str(bytes2human(memory['used'])) +
                '\n-  使用率：' + str(memory['percent']) + '%' +
                '\n-  可使用：' + str(bytes2human(memory['available'])) +
                '\n-  请及时处理。'
                ,name + '\n' + '使用率超过 ' + str(maxMemory) + '%', '内存占用过高,注意查看')
    cpu = cpuInfo()
    if cpu['cpuUse'] >= maxCpu:
        sendMSG('**' + name + '**  \n   服务器CPU：使用率达到 ' + str(maxCpu) + '%!',
                name + '\n' + 'CPU：使用率达到 ' + str(maxCpu) + '%!', 'CPU占用过高,注意查看')


# 服务器数据推送启动
def serverInfoPushStart():
    try:
        do = threading.Thread(target=serverInfoPush, )
        do.start()
        while do.is_alive():
            time.sleep(0.5)
    except Exception as r:
        logger.error(str(r))
    finally:
        threading.Timer(60, serverInfoPushStart, ()).start()


# 服务器数据推送
def serverInfoPush():
    cpu = cpuInfo()
    disks = diskInfo()
    memory = memoryInfo()
    disksIO = diskIO()
    net = netIO()
    # 先处理硬盘的信息
    diskAll = {}
    diskUse = {}
    diskFree = {}
    diskPercent = {}
    for disk in disks:
        diskAll[disk['drive']] = disk['all']
        diskUse[disk['drive']] = disk['used']
        diskFree[disk['drive']] = disk['free']
        diskPercent[disk['drive']] = disk['percent']
    # 拼装传输的json
    put = {
        "cpuCore": cpu['core'],
        "cpuLogic": cpu['logicCore'],
        "cpuUse": cpu['cpuUse'],
        "diskAll": diskAll,
        "diskUse": diskUse,
        "diskFree": diskFree,
        "diskPercent": diskPercent,
        "memoryAll": memory['all'],
        "memoryUse": memory['used'],
        "memoryFree": memory['available'],
        "memoryPercent": memory['percent'],
        "diskIORead": disksIO['read'],
        "diskIOWrite": disksIO['write'],
        "diskIOTps": disksIO['tps'],
        "netIn": net['recv'],
        "netOut": net['send'],
    }
    push(put)


# 启动
threading.Thread(target=selfHeart, ).start()
checkStateStart()
serverInfoPushStart()
sendMSGAllNo('**' + name + '**  \n  *服务器状态监控及推送程序启动*')
